#ifndef VALUE_H_
#define VALUE_H_
#define DEBUG
#include <stdio.h>
#include <set>
#include <sys/time.h> 
#include <iostream>
#include <list>
#include <map>
#include "Helpers.h" 
#include <pthread.h>
#include <vector>

const unsigned char INDEXABLE=1;
const unsigned char INT=1;
const unsigned char FLOAT=2;
const unsigned char LONG=4;
const unsigned char DOUBLE=8;
const unsigned char TEXT=16;
const unsigned char MODEL=32;
const unsigned char LIST=64;

const unsigned long NO_TRANS = 9999999;

const unsigned short IN_TRANSACTION = 1;
const unsigned short NO_TRANSACTION = 2;
const unsigned short YOUR_TRANSACTION = 3;



template<typename type>
class Value {
private:
	type value;
	long timestamp;
	unsigned char valueType;
public:
	Value(type _value,long _timestamp,unsigned char _valueType) {
		value=_value;
		timestamp=_timestamp;
		valueType=_valueType;
	}
	inline type getValue() {
		return value;
	}
	long getTimestamp() {
		return timestamp;
	}
	unsigned char getValueType() {
		return valueType;
	}
};

class ValueContainer {
protected:
	int pointer;
	unsigned char type;
public:
	ValueContainer(int _pointer,unsigned char _type) {
		pointer = _pointer;
		type=_type;
	}
	ValueContainer(ValueContainer *con) {
			pointer = con->pointer;
			type=con->type;
		}
	int getPointer() {
		return pointer;
	}
	unsigned char getType() {
		return type;
	}
};

class Listener {
protected:
	unsigned char acceptedValues;
public:
	virtual ~Listener(){}
	virtual short int valueChanged(long _id,unsigned int _index,ValueContainer *c){
		if (acceptsType(c->getType())) {
			switch(c->getType()) {
			case INT:
				return intChanged(_id,_index,(Value<int>*)c->getPointer());
			case LONG:
				return longChanged(_id,_index,(Value<long>*)c->getPointer());
			case FLOAT:
				return floatChanged(_id,_index,(Value<float>*)c->getPointer());
			case DOUBLE:
				return doubleChanged(_id,_index,(Value<double>*)c->getPointer());
			case TEXT:
				return stringChanged(_id,_index,(Value<std::string>*)c->getPointer());
			case MODEL:
				return modelChanged(_id,_index,c->getPointer());
			}
		}
		return false;
	}
	
	virtual short intChanged(long _id,unsigned int _index,Value<int> *c){
		printf("intChanged not implemented");
		return true;
	}
	virtual short longChanged(long _id,unsigned int _index,Value<long> *c){
			printf("longChanged not implemented");
			return true;
		}
	virtual short floatChanged(long _id,unsigned int _index,Value<float> *c){
			printf("floatChanged not implemented");
			return true;
		}
	virtual short doubleChanged(long _id,unsigned int _index,Value<double> *c){
			printf("doubleChanged not implemented");
			return true;
		}
	virtual short stringChanged(long _id,unsigned int _index,Value<std::string> *c){
			printf("stringChanged not implemented");
			return true;
		}
	virtual short modelChanged(long _id,unsigned int _index,int pointer){
				printf("modelChanged not implemented");
				return true;
			}
	short acceptsType(unsigned char type) {
		return (acceptedValues|type)==acceptedValues;
	}
};

class TestIntListener : public Listener {
public:
	~TestIntListener() {}
	TestIntListener() {
		acceptedValues = INT|FLOAT;
	}
	short intChanged(long _id,unsigned int _index,Value<int> *c) {
		printf("intChanged: %d\n",c->getValue());
		return true;
	}
	
};

class Indexable {
protected:
	unsigned int index;
public:
	virtual ~Indexable() {}
	unsigned int getIndex() {
		return index;
	}
	void setIndex(unsigned int _index) {
		index = _index;
	}
	virtual void closeTransaction()=0;
};

class APool {
protected:
	short inTransaction;
	std::map<pthread_t,std::list<ValueContainer*>* > *transactions;
public:
	APool() {
		inTransaction=NO_TRANSACTION;
		transactions = new std::map<pthread_t,std::list<ValueContainer*>* >;
	}
	virtual ~APool() {
		delete transactions;
	}
	virtual short hasTransaction()=0;
	
	virtual void registerProperty(ValueContainer *con)=0;
};

class AbstractModel {
public:
	virtual ~AbstractModel(){}
	virtual unsigned int registerValueHolder(std::string,Indexable *con)=0;
	virtual void setId(long id)=0;
	virtual long getId()=0;
	virtual APool* getPool()=0;
};

class AbstractPool : public APool{
public:
	virtual ~AbstractPool() {}
	virtual void registerModel(AbstractModel *_model)=0;
};


class AbstractProp: public Indexable {
protected:
	unsigned char valueType;
	bool n;
	std::set<Listener*> *constraints;
	std::set<Listener*> *consumers;
	long int id;
//	unsigned int index;
	pthread_t transaction;
	pthread_mutex_t mutex;
	AbstractModel *model;
	virtual void commitChange()=0;
	bool startTransaction() {
		pthread_mutex_lock(&mutex);
		if (transaction!=NO_TRANS && !pthread_equal(transaction,pthread_self())) {
			pthread_mutex_unlock(&mutex);
			Logger::log("locked by transaction");
			return false;
		}
		else if (model->getPool()->hasTransaction()==YOUR_TRANSACTION) {
			transaction = pthread_self();
			model->getPool()->registerProperty(new ValueContainer((int)this,INDEXABLE));
		}
		return true;
	}
public:
	AbstractProp(unsigned char _valueType,std::string _name,AbstractModel *_model,bool _isN):Indexable() {
		n=_isN;
		valueType=_valueType;
		constraints = new std::set<Listener*>;
		consumers = new std::set<Listener*>;
		id=1;
		index=2;
		_model->registerValueHolder(_name,this);
		transaction = NO_TRANS;
		model=_model;
		pthread_mutex_init(&mutex,NULL);
	}
	~AbstractProp() {
		delete constraints;
		delete consumers;
	}
		
	unsigned char getType() {
		return valueType;
	}
	
	void addConstraint(Listener *lis) {
		constraints->insert(lis);
	}
	void removeConstraint(Listener *lis) {
		constraints->erase(lis);
	}
	void addConsumer(Listener *lis) {
		consumers->insert(lis);
	}
	void removeConsumer(Listener *lis) {
		consumers->erase(lis);
	}
	
	void closeTransaction() {
			if (transaction!=NO_TRANS && pthread_equal(transaction,pthread_self())) {
				pthread_mutex_lock(&mutex);
				transaction=NO_TRANS;
				commitChange();
			}
			
		}
	
	bool isN() {
		return n;
	}
	

	
};

template<typename t>
class Prop: public AbstractProp {
private:
	Value<t> *val;
	ValueContainer *valContainer;
	Value<t> *proposal;
	ValueContainer *proposalContainer;
	bool valueSet;
public:
	Prop(unsigned char _valueType,std::string _name,AbstractModel *_model): AbstractProp(_valueType,_name,_model,false) {
		proposal=0;
		proposalContainer=0;
		val=0;
		valContainer=0;
		valueSet=false;
	}
	~Prop() {
		delete val;
		delete valContainer;
		delete proposal;
		delete proposalContainer;
	}
	
	bool isValueSet() {
		return valueSet;
	}
	
	void set(t val) {
		if (startTransaction()){
			valueSet=true;
			proposal = new Value<t>(val,TimeHelper::currentTimeMillis(),valueType);
			proposalContainer = new ValueContainer((int)proposal,valueType);
			emitChange();
		}
	}
	
	t get() {
		pthread_mutex_lock(&mutex);
		t v;
		if (transaction!=NO_TRANS && pthread_equal(transaction,pthread_self()))
			v=proposal->getValue();
		else
			v= val->getValue();
		pthread_mutex_unlock(&mutex);
		return v;
	}
	

	
	
	
protected:

	void emitChange() {
		std::set<Listener*>::const_iterator it;
		bool stop = false;
		for (it = constraints->begin(); it!=constraints->end();++it) {
			stop = !((Listener*)*it)->valueChanged(id,index,proposalContainer);
			if (stop) {
				delete proposal;
				proposal=0;
				delete proposalContainer;
				proposalContainer=0;
				pthread_mutex_unlock(&mutex);
				return;
			}
		}
		if (transaction==NO_TRANS) {
			commitChange();
		}
		else {
			pthread_mutex_unlock(&mutex);
		}
	}
	void commitChange() {
			std::set<Listener*>::const_iterator it;
			if (val!=0)
				delete val;
			val=proposal;
			if (valContainer!=0)
				delete valContainer;
			valContainer = proposalContainer;
			ValueContainer *tmp = new ValueContainer(valContainer);
			pthread_mutex_unlock(&mutex);
			for (it = consumers->begin(); it!=consumers->end();++it) {
				((Listener*)*it)->valueChanged(id,index,tmp);	
			}
			delete tmp;
		}
	
};

template<typename t>
class NProp:public AbstractProp {
private:
	
	std::vector<Value<t>* > valueList;
	std::map <int,Value<t>* > *proposals;
	Value<t> *proposal;
	ValueContainer *proposalContainer;
	
public:
	NProp(unsigned char _valueType,std::string _name,AbstractModel *_model):AbstractProp(_valueType,_name,_model,true) {
		proposal=0;
	}
	~NProp() {
		delete proposal;
		delete proposalContainer;
		delete proposals;

	}
	
	void set(int i,t val) {
		if (startTransaction()) {
			proposal = new Value<t>(val,TimeHelper::currentTimeMillis(),valueType);
			proposalContainer = new ValueContainer((int)proposal,valueType);
			proposals->insert(std::pair<int,Value<t> >(i,proposal));
			emitChange();
		}
	}
	
	void append(t val) {
		if (startTransaction()) {
					proposal = new Value<t>(val,TimeHelper::currentTimeMillis(),valueType);
					proposalContainer = new ValueContainer((int)proposal,valueType);
//					proposals->insert(std::pair<int,Value<t>* >(valueList.size(),proposal));
					emitChange();
				}
	}
	
	t get(int i) {
			pthread_mutex_lock(&mutex);
			t v;
			if (transaction!=NO_TRANS && pthread_equal(transaction,pthread_self()) && false)
				Logger::log("error in transaction, n implemented yet"); // TODO implement
			else {
				//TODO rangeCheck
				Value<t> *_v = valueList[i];
				v= _v->getValue();
			}
			pthread_mutex_unlock(&mutex);
			return v;
		}
	
	int size() {
		return valueList.size();
	}
	
	void remove(int i) {
		//TODO make threadsafe
		valueList.erase(valueList.begin()+i);
	}
	
	
protected:

	void emitChange() {
		std::set<Listener*>::const_iterator it;
		bool stop = false;
		for (it = constraints->begin(); it!=constraints->end();++it) {
			stop = !((Listener*)*it)->valueChanged(id,index,proposalContainer);
			if (stop) {
				delete proposal;
				proposal=0;
				delete proposalContainer;
				proposalContainer=0;
				pthread_mutex_unlock(&mutex);
				return;
			}
		}
		if (transaction==NO_TRANS) {
			commitChange();
		}
		else {
			pthread_mutex_unlock(&mutex);
		}
	}
	void commitChange() {
			std::set<Listener*>::const_iterator it;
//			if (val!=0)
//				delete val;
			valueList.push_back(proposal);

			ValueContainer *tmp = new ValueContainer((int)proposal,valueType);
			pthread_mutex_unlock(&mutex);
			for (it = consumers->begin(); it!=consumers->end();++it) {
				((Listener*)*it)->valueChanged(id,index,tmp);	
			}
			delete tmp;
		}
	
};



class Model:public AbstractModel{
private:
	std::string name;
	std::map<std::string,AbstractProp*> *valueHolderMap;
	std::list<AbstractProp*> *valueHolders;
	long id;
	AbstractPool *pool;
	std::string className;
public:
	Model(AbstractPool *_pool, std::string _name,std::string _className) {
		className=_className;
		pool = _pool;
		name = _name;
		_pool->registerModel(this);
		valueHolderMap = new std::map<std::string,AbstractProp*>;
		valueHolders = new std::list<AbstractProp*>;
	}
	~Model() {
		std::list<AbstractProp*>::iterator it;
		for (it = valueHolders->begin(); it!=valueHolders->end();++it) {
			AbstractProp *con = (AbstractProp*)*it;
			delete con;
		}
		delete valueHolderMap;
		delete valueHolders;
	}

	std::string getClassName() {
		return className;
	}
	
	APool* getPool() {
		return pool;
	}
	
	void setId(long _id) {
		id=_id;
	}
	
	long getId() {
		return id;
	}
	
	unsigned int registerValueHolder(std::string _tName,Indexable *c) {
		AbstractProp *con = (AbstractProp*)c;
		unsigned int index = valueHolders->size()-1;
		valueHolders->push_back(con);
		valueHolderMap->insert(std::pair<std::string,AbstractProp*>(_tName,con));
		con->setIndex(index);
		return index;
	}
	
	std::list<AbstractProp*>* getProps() {
		return valueHolders;
	}
	
	AbstractProp* getProp(unsigned int index) {
			std::list<AbstractProp*>::iterator it = valueHolders->begin();
			unsigned int i;
			for (i=0;i<index;i++) {
				it++;
			}
			return (AbstractProp*)*it;
//		return valueHolders[index];
		}
		
	AbstractProp* getProp(std::string _name) {
			return valueHolderMap->at(_name);
		}
	
		bool setInt(std::string _name,int val) {
			AbstractProp *con = getProp(_name);
				if (con->getType()!=INT)
					return false;
				Prop<int> *prop = (Prop<int>*)con;
				prop->set(val);
				return true;
			}
			
			bool setLong(std::string _name,long val) {
				AbstractProp *con = getProp(_name);
					if (con->getType()!=LONG)
						return false;
					Prop<long> *prop = (Prop<long>*)con;
					prop->set(val);
					return true;
				}
			
			bool setFloat(std::string _name,float val) {
				AbstractProp *con = getProp(_name);
					if (con->getType()!=FLOAT)
						return false;
					Prop<float> *prop = (Prop<float>*)con;
					prop->set(val);
					return true;
				}
			
			bool setDouble(std::string _name,double val) {
				AbstractProp *con = getProp(_name);
					if (con->getType()!=DOUBLE)
						return false;
					Prop<double> *prop = (Prop<double>*)con;
					prop->set(val);
					return true;
				}
			
			bool setString(std::string _name,std::string val) {
				AbstractProp *con = getProp(_name);
					if (con->getType()!=TEXT)
						return false;
					Prop<std::string> *prop = (Prop<std::string>*)con;
					prop->set(val);
					return true;
				}
		
			bool setModel(std::string _name,Model *val) {
				AbstractProp *con = getProp(_name);
								if (con->getType()!=MODEL)
									return false;
								Prop<Model*> *prop = (Prop<Model*>*)con;
								prop->set(val);
								return true;
							}
			
	bool setInt(unsigned int _index,int val) {
		AbstractProp *con = getProp(_index);
		if (con->getType()!=INT)
			return false;
		Prop<int> *prop = (Prop<int>*)con;
		prop->set(val);
		return true;
	}
	
	bool setLong(unsigned int _index,long val) {
		AbstractProp *con = getProp(_index);
			if (con->getType()!=LONG)
				return false;
			Prop<long> *prop = (Prop<long>*)con;
			prop->set(val);
			return true;
		}
	
	bool setFloat(unsigned int _index,float val) {
		AbstractProp *con = getProp(_index);
			if (con->getType()!=FLOAT)
				return false;
			Prop<float> *prop = (Prop<float>*)con;
			prop->set(val);
			return true;
		}
	
	bool setDouble(unsigned int _index,double val) {
		AbstractProp *con = getProp(_index);
			if (con->getType()!=DOUBLE)
				return false;
			Prop<double> *prop = (Prop<double>*)con;
			prop->set(val);
			return true;
		}
	
	bool setString(unsigned int _index,std::string val) {
		AbstractProp *con = getProp(_index);
			if (con->getType()!=TEXT)
				return false;
			Prop<std::string> *prop = (Prop<std::string>*)con;
			prop->set(val);
			return true;
		}
	
	bool setModel(unsigned int _index,Model *val) {
		AbstractProp *con = getProp(_index);
				if (con->getType()!=MODEL)
					return false;
				Prop<Model*> *prop = (Prop<Model*>*)con;
				prop->set(val);
				return true;
			}
	
	int getInt(unsigned int _index) {
		AbstractProp *con = getProp(_index);
		Prop<int> *prop = (Prop<int>*)con;
		return prop->get();
	}
	
	float getFloat(unsigned int _index) {
		AbstractProp *con = getProp(_index);
		Prop<float> *prop = (Prop<float>*)con;
		return prop->get();
	}
	
	double getDouble(unsigned int _index) {
		AbstractProp *con = getProp(_index);
		Prop<double> *prop = (Prop<double>*)con;
		return prop->get();
	}
	
	long getLong(unsigned int _index) {
		AbstractProp *con = getProp(_index);
		Prop<long> *prop = (Prop<long>*)con;
		return prop->get();
	}
	
	std::string getString(unsigned int _index) {
		AbstractProp *con = getProp(_index);
		Prop<std::string> *prop = (Prop<std::string>*)con;
		return prop->get();
	}
	
	Model* getModel(unsigned int _index) {
		AbstractProp *con = getProp(_index);
			Prop<Model*> *prop = (Prop<Model*>*)con;
			return prop->get();
		}
	
	
	int getInt(std::string _name) {
		AbstractProp *con = getProp(_name);
		Prop<int> *prop = (Prop<int>*)con;
		return prop->get();
	}
		
	float getFloat(std::string _name) {
		AbstractProp *con = getProp(_name);
		Prop<float> *prop = (Prop<float>*)con;
		return prop->get();
	}
	
	double getDouble(std::string _name) {
		AbstractProp *con = getProp(_name);
		Prop<double> *prop = (Prop<double>*)con;
		return prop->get();
	}
	
	long getLong(std::string _name) {
		AbstractProp *con = getProp(_name);
		Prop<long> *prop = (Prop<long>*)con;
		return prop->get();
	}
	
	std::string getString(std::string _name) {
		AbstractProp *con = getProp(_name);
		Prop<std::string> *prop = (Prop<std::string>*)con;
		return prop->get();
	}
	
	Model* getModel(std::string _name) {
		AbstractProp *con = getProp(_name);
			Prop<Model*> *prop = (Prop<Model*>*)con;
			return prop->get();
		}
	std::string getName() {
		return name;
	}
};




#endif /*VALUE_H_*/
